Abstract: The goal of this study is to look at territory size of the Louisiana Waterthrush during breeding season at three locations; Wall Branch stream located in Rotary Park in Clarksville Tennessee 37040, Dry Creek located in Cheatham Wildlife Management Area in Ashland City Tennessee 37015, Big Hollow located in Beaman Park in Ashland City Tennessee 37015. Mapping these territories in these streams will show not only the territories of the Louisiana Waterthrush present but also possible areas of concentrated use that may aid in the location of their nesting sites.

We will be making KDE maps so we will need to register with google maps to use their satelite version of our map area. The link below is where a lot of the following code came from. https://cfss.uchicago.edu/notes/raster-maps-with-ggmap/ You must register for google maps to get an AI (I used the one year free trial for this and they don’t auto charge once the year is over). OpenMapSource is another option as well if your java supports this. Once you have registered create your base map using google maps address or coordinates.

register_google("AIzaSyDKCi-txHXwSLjB-UURgX1eLzhNVcsXMRg")
basemap<- get_map(location = "1539 Dry Creek Rd
Ashland City, TN 37015", zoom=15)
Source : https://maps.googleapis.com/maps/api/staticmap?center=1539%20Dry%20Creek%20Rd%0AAshland%20City,%20TN%2037015&zoom=15&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx-txHXwSLjB-UURgX1eLzhNVcsXMRg
Source : https://maps.googleapis.com/maps/api/geocode/json?address=1539+Dry+Creek+Rd%0AAshland+City,+TN+37015&key=xxx-txHXwSLjB-UURgX1eLzhNVcsXMRg
ggmap(basemap)

Next, we can put our LOWA encounter cooridinates on the basemap we just created.

DCmap<-ggmap(basemap)+ geom_point(data=DryCreek, aes(Longitude, Latitude)) +
  geom_point(data = DryCreek, aes(Longitude, Latitude), size = 0.1) +
  theme(axis.title = element_text(face="bold")) + labs(x="Longitude", y="Latitude") +
  theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) + theme_bw()
plot(DCmap)

Now it’s time to create KDE and MCP, but first we have to format a few things. It will be important to first put your data into SpatialPoints we will be using CRS (coordinate reference system) which is the format for most GPS data. The link below is where a lot of the following code came from. https://mhallwor.github.io/_pages/activities_GenerateTerritories

DCpts <- sp::SpatialPoints(coords = cbind(DryCreek$Longitude, DryCreek$Latitude),
                                proj4string = sp::CRS("+init=epsg:4326"))
head(DCpts)
class       : SpatialPoints 
features    : 1 
extent      : -87.18876, -87.18876, 36.18876, 36.18876  (xmin, xmax, ymin, ymax)
coord. ref. : +init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
## put all the data into a single SpatialPointsDataFrame (spdf)
DC_spdf <- sp::SpatialPointsDataFrame(DCpts, DryCreek)
head(DC_spdf)
#although we don't have seperate species we need to let R know that all points are for one stream system and this will create a list of one
DC_sep <- split(x = DC_spdf, f = DC_spdf$Name, drop = FALSE)

Time to make the MCP.

DCmcp <- lapply(DC_sep, FUN = function(x){rgeos::gConvexHull(x)})

##this makes polygon from the list of one when we told R that all variables are from one stream system
DCmcp <- mapply(DCmcp, names(DCmcp), 
                  SIMPLIFY = FALSE,
                  FUN = function(x,y){x@polygons[[1]]@ID <- y
                  return(x)})

DCmcp <- do.call(rbind,DCmcp)
DCmcp <- SpatialPolygonsDataFrame(Sr = DCmcp,
                                   data = data.frame(Bird = names(DCmcp)),
                                   match.ID = FALSE)
plot(DCmcp)

Now that we have an MCP for the stream, onto KDE.

## Step one: do least squares cross-validation to estimate bandwidth (you may get a warning message but keep going)
bw <- lapply(DC_sep, FUN = function(x){ks::Hlscv(x@coords)})
Data contain duplicated values: LSCV is not well-behaved in this case
## Step two: generate kde

DC_kde <-mapply(DC_sep,bw,
                     SIMPLIFY = FALSE,
                     FUN = function(x,y){
                       raster(kde(x@coords,h=y))})
# This code makes a custom function called getContour. 
# Inputs:
#    kde = kernel density estimate
#    prob = probabily - default is 0.95

getContour <- function(kde, prob = 0.95){
  # set all values 0 to NA
  kde[kde == 0]<-NA
  # create a vector of raster values
  kde_values <- raster::getValues(kde)
  # sort values 
  sortedValues <- sort(kde_values[!is.na(kde_values)],decreasing = TRUE)
  # find cumulative sum up to ith location
  sums <- cumsum(as.numeric(sortedValues))
  # binary response is value in the probabily zone or not
  p <- sum(sums <= prob * sums[length(sums)])
  # Set values in raster to 1 or 0
  kdeprob <- raster::setValues(kde, kde_values >= sortedValues[p])
  # return new kde
  return(kdeprob)}

DC_95kde <- lapply(DC_kde,
                    FUN = getContour,prob = 0.95)

These next plots put MCP on top of KDE

plot(DC_kde[[1]])+
plot(DCmcp[1,],add = TRUE)
integer(0)

Time to overlap KDE onto the base map. Code found from http://data-analytics.net/cep/Schedule_files/geospatial.html

KDEmap<-DCmap +
  stat_density2d(aes(x = Longitude, y = Latitude, fill = ..level..,alpha=..level..), bins = 10, geom = "polygon", data = DryCreek) +
  scale_fill_gradient(low = "red", high = "green")+
  ggtitle("Dry Creek, TN")
Compleated KDE Heatmap

Compleated KDE Heatmap

Big Hollow, TN

Big_Hollow <- read_csv("/Users/Kirstenglish/Desktop/Big Hollow.csv")
Parsed with column specification:
cols(
  Name = col_character(),
  Latitude = col_double(),
  Longitude = col_double(),
  Number = col_double()
)
View(Big_Hollow)

Just like before, retrieve a basemap of your site location.

basemap<- get_map(location = "Ridgetop Trail
Ashland City, TN 37015", zoom=15)
Source : https://maps.googleapis.com/maps/api/staticmap?center=Ridgetop%20Trail%0AAshland%20City,%20TN%2037015&zoom=15&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx-txHXwSLjB-UURgX1eLzhNVcsXMRg
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Ridgetop+Trail%0AAshland+City,+TN+37015&key=xxx-txHXwSLjB-UURgX1eLzhNVcsXMRg
ggmap(basemap)

Put our LOWA encounter cooridinates on the basemap we just created.

#time to put points on the map
BHmap<-ggmap(basemap)+ geom_point(data=Big_Hollow, aes(Longitude, Latitude)) +
  geom_point(data = Big_Hollow, aes(Longitude, Latitude), size = 0.1) +
  theme(axis.title = element_text(face="bold")) + labs(x="Longitude", y="Latitude") +
  theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) + theme_bw()
plot(BHmap)

Put data into spatial points like before to be able to create the KDE and MCP. we will be using CRS (coordinate reference system) which is the format for most GPS data.

Time to make the MCP.

BHmcp <- lapply(BH_sep, FUN = function(x){rgeos::gConvexHull(x)})

##this makes polygon from the list of one when we told R that all variables are from one stream system
BHmcp <- mapply(BHmcp, names(BHmcp), 
                SIMPLIFY = FALSE,
                FUN = function(x,y){x@polygons[[1]]@ID <- y
                return(x)})

BHmcp <- do.call(rbind,BHmcp)
BHmcp <- SpatialPolygonsDataFrame(Sr = BHmcp,
                                  data = data.frame(Bird = names(BHmcp)),
                                  match.ID = FALSE)
plot(BHmcp)

Now that we have an MCP for the stream, onto KDE.

bw <- lapply(BH_sep, FUN = function(x){ks::Hlscv(x@coords)})
## Step two: generate kde

BH_kde <-mapply(BH_sep,bw,
                SIMPLIFY = FALSE,
                FUN = function(x,y){
                  raster(kde(x@coords,h=y))})

# This code makes a custom function called getContour. 
# Inputs:
#    kde = kernel density estimate
#    prob = probabily - default is 0.95

getContour <- function(kde, prob = 0.95){
  # set all values 0 to NA
  kde[kde == 0]<-NA
  # create a vector of raster values
  kde_values <- raster::getValues(kde)
  # sort values 
  sortedValues <- sort(kde_values[!is.na(kde_values)],decreasing = TRUE)
  # find cumulative sum up to ith location
  sums <- cumsum(as.numeric(sortedValues))
  # binary response is value in the probabily zone or not
  p <- sum(sums <= prob * sums[length(sums)])
  # Set values in raster to 1 or 0
  kdeprob <- raster::setValues(kde, kde_values >= sortedValues[p])
  # return new kde
  return(kdeprob)}

BH_95kde <- lapply(BH_kde,
                   FUN = getContour,prob = 0.95)

These next plots put MCP on top of KDE


plot(BH_kde[[1]])+
  plot(BHmcp[1,],add = TRUE)
integer(0)

Time to overlap KDE onto the base map. Code found from http://data-analytics.net/cep/Schedule_files/geospatial.html

Compleated KDE Heatmap

Compleated KDE Heatmap

Wall Branch, TN

Create the basemap for the site location. This site is Wall Branch stream in Tennesse

basemap<- get_map((location = " 2561 Alex Overlook Way
Clarksville, TN 37043, 36.497818, -87.267428"), zoom=15, maptype = "satellite")
Source : https://maps.googleapis.com/maps/api/staticmap?center=%202561%20Alex%20Overlook%20Way%0AClarksville,%20TN%2037043,%2036.497818,%20-87.267428&zoom=15&size=640x640&scale=2&maptype=satellite&language=en-EN&key=xxx-txHXwSLjB-UURgX1eLzhNVcsXMRg
Source : https://maps.googleapis.com/maps/api/geocode/json?address=2561+Alex+Overlook+Way%0AClarksville,+TN+37043,+36.497818,+-87.267428&key=xxx-txHXwSLjB-UURgX1eLzhNVcsXMRg
ggmap(basemap)

LOWA encounter cooridinates on the basemap we just created.

#time to put points on the map
WBmap<-ggmap(basemap)+ geom_point(data=Wall_Branch, aes(Longitude, Latitude)) +
  geom_point(data = Wall_Branch, aes(Longitude, Latitude), color= "black", size = 0.1) +
  theme(axis.title = element_text(face="bold")) + labs(x="Longitude", y="Latitude") +
  theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) + theme_classic()
plot(WBmap)

WBpts <- sp::SpatialPoints(coords = cbind(Wall_Branch$Longitude, Wall_Branch$Latitude),
                           proj4string = sp::CRS("+init=epsg:4326"))
head(WBpts)
class       : SpatialPoints 
features    : 1 
extent      : -87.26923, -87.26923, 36.49896, 36.49896  (xmin, xmax, ymin, ymax)
coord. ref. : +init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
WB_spdf <- sp::SpatialPointsDataFrame(WBpts, Wall_Branch)
head(WB_spdf)

WB_sep <- split(x = WB_spdf, f = WB_spdf$Name, drop = FALSE)

Time to make the MCP.

WBmcp <- lapply(WB_sep, FUN = function(x){rgeos::gConvexHull(x)})

##this makes polygon from the list of one when we told R that all variables are from one stream system
WBmcp <- mapply(WBmcp, names(WBmcp), 
                SIMPLIFY = FALSE,
                FUN = function(x,y){x@polygons[[1]]@ID <- y
                return(x)})
WBmcp <- do.call(rbind,WBmcp)
WBmcp <- SpatialPolygonsDataFrame(Sr = WBmcp,
                                  data = data.frame(Bird = names(WBmcp)),
                                  match.ID = FALSE)
plot(WBmcp)

Now that we have an MCP for the stream, onto KDE.

bw <- lapply(WB_sep, FUN = function(x){ks::Hlscv(x@coords)})

## Step two: generate kde
WB_kde <-mapply(WB_sep,bw,
                SIMPLIFY = FALSE,
                FUN = function(x,y){
                  raster(kde(x@coords,h=y))})
# This code makes a custom function called getContour. 
# Inputs:
#    kde = kernel density estimate
#    prob = probabily - default is 0.95

getContour <- function(kde, prob = 0.95){
  # set all values 0 to NA
  kde[kde == 0]<-NA
  # create a vector of raster values
  kde_values <- raster::getValues(kde)
  # sort values 
  sortedValues <- sort(kde_values[!is.na(kde_values)],decreasing = TRUE)
  # find cumulative sum up to ith location
  sums <- cumsum(as.numeric(sortedValues))
  # binary response is value in the probabily zone or not
  p <- sum(sums <= prob * sums[length(sums)])
  # Set values in raster to 1 or 0
  kdeprob <- raster::setValues(kde, kde_values >= sortedValues[p])
  # return new kde
  return(kdeprob)}

WB_95kde <- lapply(WB_kde,
                   FUN = getContour,prob = 0.95)

These next plots put MCP on top of KDE

## These next plots put MCP on top of KDE, (territory for this map is very linear)
plot(WB_kde[[1]])+
  plot(WBmcp[1,],add = TRUE)
integer(0)

Time to overlap KDE onto the base map. Code found from http://data-analytics.net/cep/Schedule_files/geospatial.html

WBkde<-WBmap +
  stat_density2d(aes(x = Longitude, y = Latitude, fill = ..level..,alpha=..level..), bins = 10, geom = "polygon", data = Wall_Branch) +
  scale_fill_gradient(low = "red", high = "green")+
  ggtitle("Wall Branch, TN")
Compleated KDE Heatmap

Compleated KDE Heatmap

LS0tCnRpdGxlOiAiVGVyaXRvcnkgTWFwcGluZyBvZiB0aGUgTG91aXNpYW5hIFdhdGVydGhydXNoIGF0IHRocmVlIFRlbm5lc2VlIGxvY2F0aW9ucyIKYXV0aG9yOiAiS2lyc3RlbiBFbmdsaXNoIgpkYXRlOiAiMTAvMzAvMjAxOSIKb3V0cHV0OiBodG1sX25vdGVib29rCgotLS0KCkFic3RyYWN0OgogIFRoZSBnb2FsIG9mIHRoaXMgc3R1ZHkgaXMgdG8gbG9vayBhdCB0ZXJyaXRvcnkgc2l6ZSBvZiB0aGUgTG91aXNpYW5hIFdhdGVydGhydXNoIGR1cmluZyBicmVlZGluZyBzZWFzb24gYXQgdGhyZWUgbG9jYXRpb25zOyBXYWxsIEJyYW5jaCBzdHJlYW0gbG9jYXRlZCBpbiBSb3RhcnkgUGFyayBpbiBDbGFya3N2aWxsZSBUZW5uZXNzZWUgMzcwNDAsIERyeSBDcmVlayBsb2NhdGVkIGluIENoZWF0aGFtIFdpbGRsaWZlIE1hbmFnZW1lbnQgQXJlYSBpbiBBc2hsYW5kIENpdHkgVGVubmVzc2VlIDM3MDE1LCBCaWcgSG9sbG93IGxvY2F0ZWQgaW4gQmVhbWFuIFBhcmsgaW4gQXNobGFuZCBDaXR5IFRlbm5lc3NlZSAzNzAxNS4gTWFwcGluZyB0aGVzZSB0ZXJyaXRvcmllcyBpbiB0aGVzZSBzdHJlYW1zIHdpbGwgc2hvdyBub3Qgb25seSB0aGUgdGVycml0b3JpZXMgb2YgdGhlIExvdWlzaWFuYSBXYXRlcnRocnVzaCBwcmVzZW50IGJ1dCBhbHNvIHBvc3NpYmxlIGFyZWFzIG9mIGNvbmNlbnRyYXRlZCB1c2UgdGhhdCBtYXkgYWlkIGluIHRoZSBsb2NhdGlvbiBvZiB0aGVpciBuZXN0aW5nIHNpdGVzLiAKCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShNQVNTKQpsaWJyYXJ5KE9wZW5TdHJlZXRNYXApCmxpYnJhcnkocmdlb3MpCmxpYnJhcnkoZ2dmb3J0aWZ5KQpsaWJyYXJ5KHNwKQpsaWJyYXJ5KGtzKQpsaWJyYXJ5KHJhc3RlcikKbGlicmFyeShnZ21hcCkKCkRyeUNyZWVrIDwtIHJlYWRfY3N2KCIvVXNlcnMvS2lyc3RlbmdsaXNoL0Rlc2t0b3AvSW5kZXBlbmRlbnQgc3R1ZHkvTUFQUy9EcnkgQ3JlZWsuY3N2IikKVmlldyhEcnlDcmVlaykKYGBgCgoKV2Ugd2lsbCBiZSBtYWtpbmcgS0RFIG1hcHMgc28gd2Ugd2lsbCBuZWVkIHRvIHJlZ2lzdGVyIHdpdGggZ29vZ2xlIG1hcHMgdG8gdXNlIHRoZWlyIHNhdGVsaXRlIHZlcnNpb24gb2Ygb3VyIG1hcCBhcmVhLiBUaGUgbGluayBiZWxvdyBpcyB3aGVyZSBhIGxvdCBvZiB0aGUgZm9sbG93aW5nIGNvZGUgY2FtZSBmcm9tLiBodHRwczovL2Nmc3MudWNoaWNhZ28uZWR1L25vdGVzL3Jhc3Rlci1tYXBzLXdpdGgtZ2dtYXAvCjxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj5Zb3UgbXVzdCByZWdpc3RlciBmb3IgZ29vZ2xlIG1hcHMgdG8gZ2V0IGFuIEFJIChJIHVzZWQgdGhlIG9uZSB5ZWFyIGZyZWUgdHJpYWwgZm9yIHRoaXMgYW5kIHRoZXkgZG9uJ3QgYXV0byBjaGFyZ2Ugb25jZSB0aGUgeWVhciBpcyBvdmVyKS4gT3Blbk1hcFNvdXJjZSBpcyBhbm90aGVyIG9wdGlvbiBhcyB3ZWxsIGlmIHlvdXIgamF2YSBzdXBwb3J0cyB0aGlzLiBPbmNlIHlvdSBoYXZlIHJlZ2lzdGVyZWQgY3JlYXRlIHlvdXIgYmFzZSBtYXAgdXNpbmcgZ29vZ2xlIG1hcHMgYWRkcmVzcyBvciBjb29yZGluYXRlcy48L3NwYW4+CgoKCmBgYHtyIEdldCBiYXNlbWFwfQpyZWdpc3Rlcl9nb29nbGUoIkFJemFTeURLQ2ktdHhIWHdTTGpCLVVVUmdYMWVMemhOVmNzWE1SZyIpCmJhc2VtYXA8LSBnZXRfbWFwKGxvY2F0aW9uID0gIjE1MzkgRHJ5IENyZWVrIFJkCkFzaGxhbmQgQ2l0eSwgVE4gMzcwMTUiLCB6b29tPTE1KQpnZ21hcChiYXNlbWFwKQpgYGAKCjxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj5OZXh0LCB3ZSBjYW4gcHV0IG91ciBMT1dBIGVuY291bnRlciBjb29yaWRpbmF0ZXMgb24gdGhlIGJhc2VtYXAgd2UganVzdCBjcmVhdGVkLjwvc3Bhbj4KCgpgYGB7ciBCYXNlIG1hcCB3aXRoIExPV0Fsb2NhdGlvbiBwb2ludHMsIHdhcm5pbmc9RkFMU0V9CkRDbWFwPC1nZ21hcChiYXNlbWFwKSsgZ2VvbV9wb2ludChkYXRhPURyeUNyZWVrLCBhZXMoTG9uZ2l0dWRlLCBMYXRpdHVkZSkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBEcnlDcmVlaywgYWVzKExvbmdpdHVkZSwgTGF0aXR1ZGUpLCBzaXplID0gMC4xKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpICsgbGFicyh4PSJMb25naXR1ZGUiLCB5PSJMYXRpdHVkZSIpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0xKSkgKyB0aGVtZV9idygpCnBsb3QoRENtYXApCmBgYAoKCjxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4gTm93IGl0J3MgdGltZSB0byBjcmVhdGUgS0RFIGFuZCBNQ1AsIGJ1dCBmaXJzdCB3ZSBoYXZlIHRvIGZvcm1hdCBhIGZldyB0aGluZ3MuIEl0IHdpbGwgYmUgaW1wb3J0YW50IHRvIGZpcnN0IHB1dCB5b3VyIGRhdGEgaW50byBTcGF0aWFsUG9pbnRzIAp3ZSB3aWxsIGJlIHVzaW5nIENSUyAgKGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbSkgd2hpY2ggaXMgdGhlIGZvcm1hdCBmb3IgbW9zdCBHUFMgZGF0YS4gVGhlIGxpbmsgYmVsb3cgaXMgd2hlcmUgYSBsb3Qgb2YgdGhlIGZvbGxvd2luZyBjb2RlIGNhbWUgZnJvbS4gIGh0dHBzOi8vbWhhbGx3b3IuZ2l0aHViLmlvL19wYWdlcy9hY3Rpdml0aWVzX0dlbmVyYXRlVGVycml0b3JpZXMgPC9zcGFuPgoKYGBge3IgU3BhdGlhbCBQb2ludHMgZm9ybWF0dGluZ30KRENwdHMgPC0gc3A6OlNwYXRpYWxQb2ludHMoY29vcmRzID0gY2JpbmQoRHJ5Q3JlZWskTG9uZ2l0dWRlLCBEcnlDcmVlayRMYXRpdHVkZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmcgPSBzcDo6Q1JTKCIraW5pdD1lcHNnOjQzMjYiKSkKaGVhZChEQ3B0cykKCiMjIHB1dCBhbGwgdGhlIGRhdGEgaW50byBhIHNpbmdsZSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lIChzcGRmKQpEQ19zcGRmIDwtIHNwOjpTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKERDcHRzLCBEcnlDcmVlaykKaGVhZChEQ19zcGRmKQojYWx0aG91Z2ggd2UgZG9uJ3QgaGF2ZSBzZXBlcmF0ZSBzcGVjaWVzIHdlIG5lZWQgdG8gbGV0IFIga25vdyB0aGF0IGFsbCBwb2ludHMgYXJlIGZvciBvbmUgc3RyZWFtIHN5c3RlbSBhbmQgdGhpcyB3aWxsIGNyZWF0ZSBhIGxpc3Qgb2Ygb25lCkRDX3NlcCA8LSBzcGxpdCh4ID0gRENfc3BkZiwgZiA9IERDX3NwZGYkTmFtZSwgZHJvcCA9IEZBTFNFKQpgYGAKCjxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4gVGltZSB0byBtYWtlIHRoZSBNQ1AuPC9zcGFuPgoKYGBge3IgTUNQfQpEQ21jcCA8LSBsYXBwbHkoRENfc2VwLCBGVU4gPSBmdW5jdGlvbih4KXtyZ2Vvczo6Z0NvbnZleEh1bGwoeCl9KQoKIyN0aGlzIG1ha2VzIHBvbHlnb24gZnJvbSB0aGUgbGlzdCBvZiBvbmUgd2hlbiB3ZSB0b2xkIFIgdGhhdCBhbGwgdmFyaWFibGVzIGFyZSBmcm9tIG9uZSBzdHJlYW0gc3lzdGVtCkRDbWNwIDwtIG1hcHBseShEQ21jcCwgbmFtZXMoRENtY3ApLCAKICAgICAgICAgICAgICAgICAgU0lNUExJRlkgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCx5KXt4QHBvbHlnb25zW1sxXV1ASUQgPC0geQogICAgICAgICAgICAgICAgICByZXR1cm4oeCl9KQoKRENtY3AgPC0gZG8uY2FsbChyYmluZCxEQ21jcCkKRENtY3AgPC0gU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lKFNyID0gRENtY3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEuZnJhbWUoQmlyZCA9IG5hbWVzKERDbWNwKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF0Y2guSUQgPSBGQUxTRSkKcGxvdChEQ21jcCkKYGBgCgo8c3BhbiBzdHlsZT0iY29sb3I6cGluayI+Tm93IHRoYXQgd2UgaGF2ZSBhbiBNQ1AgZm9yIHRoZSBzdHJlYW0sIG9udG8gS0RFLjwvc3Bhbj4KCmBgYHtyIEtERX0KIyMgU3RlcCBvbmU6IGRvIGxlYXN0IHNxdWFyZXMgY3Jvc3MtdmFsaWRhdGlvbiB0byBlc3RpbWF0ZSBiYW5kd2lkdGggKHlvdSBtYXkgZ2V0IGEgd2FybmluZyBtZXNzYWdlIGJ1dCBrZWVwIGdvaW5nKQpidyA8LSBsYXBwbHkoRENfc2VwLCBGVU4gPSBmdW5jdGlvbih4KXtrczo6SGxzY3YoeEBjb29yZHMpfSkKIyMgU3RlcCB0d286IGdlbmVyYXRlIGtkZQoKRENfa2RlIDwtbWFwcGx5KERDX3NlcCxidywKICAgICAgICAgICAgICAgICAgICAgU0lNUExJRlkgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCx5KXsKICAgICAgICAgICAgICAgICAgICAgICByYXN0ZXIoa2RlKHhAY29vcmRzLGg9eSkpfSkKIyBUaGlzIGNvZGUgbWFrZXMgYSBjdXN0b20gZnVuY3Rpb24gY2FsbGVkIGdldENvbnRvdXIuIAojIElucHV0czoKIyAgICBrZGUgPSBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZQojICAgIHByb2IgPSBwcm9iYWJpbHkgLSBkZWZhdWx0IGlzIDAuOTUKCmdldENvbnRvdXIgPC0gZnVuY3Rpb24oa2RlLCBwcm9iID0gMC45NSl7CiAgIyBzZXQgYWxsIHZhbHVlcyAwIHRvIE5BCiAga2RlW2tkZSA9PSAwXTwtTkEKICAjIGNyZWF0ZSBhIHZlY3RvciBvZiByYXN0ZXIgdmFsdWVzCiAga2RlX3ZhbHVlcyA8LSByYXN0ZXI6OmdldFZhbHVlcyhrZGUpCiAgIyBzb3J0IHZhbHVlcyAKICBzb3J0ZWRWYWx1ZXMgPC0gc29ydChrZGVfdmFsdWVzWyFpcy5uYShrZGVfdmFsdWVzKV0sZGVjcmVhc2luZyA9IFRSVUUpCiAgIyBmaW5kIGN1bXVsYXRpdmUgc3VtIHVwIHRvIGl0aCBsb2NhdGlvbgogIHN1bXMgPC0gY3Vtc3VtKGFzLm51bWVyaWMoc29ydGVkVmFsdWVzKSkKICAjIGJpbmFyeSByZXNwb25zZSBpcyB2YWx1ZSBpbiB0aGUgcHJvYmFiaWx5IHpvbmUgb3Igbm90CiAgcCA8LSBzdW0oc3VtcyA8PSBwcm9iICogc3Vtc1tsZW5ndGgoc3VtcyldKQogICMgU2V0IHZhbHVlcyBpbiByYXN0ZXIgdG8gMSBvciAwCiAga2RlcHJvYiA8LSByYXN0ZXI6OnNldFZhbHVlcyhrZGUsIGtkZV92YWx1ZXMgPj0gc29ydGVkVmFsdWVzW3BdKQogICMgcmV0dXJuIG5ldyBrZGUKICByZXR1cm4oa2RlcHJvYil9CgpEQ185NWtkZSA8LSBsYXBwbHkoRENfa2RlLAogICAgICAgICAgICAgICAgICAgIEZVTiA9IGdldENvbnRvdXIscHJvYiA9IDAuOTUpCgpgYGAKCjxzcGFuIHN0eWxlPSJjb2xvcjpwaW5rIj5UaGVzZSBuZXh0IHBsb3RzIHB1dCBNQ1Agb24gdG9wIG9mIEtERQo8L3NwYW4+CmBgYHtyIE1hcH0KcGxvdChEQ19rZGVbWzFdXSkrCnBsb3QoRENtY3BbMSxdLGFkZCA9IFRSVUUpCmBgYAo8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+IFRpbWUgdG8gb3ZlcmxhcCBLREUgb250byB0aGUgYmFzZSBtYXAuIENvZGUgZm91bmQgZnJvbSBodHRwOi8vZGF0YS1hbmFseXRpY3MubmV0L2NlcC9TY2hlZHVsZV9maWxlcy9nZW9zcGF0aWFsLmh0bWwgPC9zcGFuPgoKCmBgYHtyIEtERSB3aXRoIG1hcH0KS0RFbWFwPC1EQ21hcCArCiAgc3RhdF9kZW5zaXR5MmQoYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSwgZmlsbCA9IC4ubGV2ZWwuLixhbHBoYT0uLmxldmVsLi4pLCBiaW5zID0gMTAsIGdlb20gPSAicG9seWdvbiIsIGRhdGEgPSBEcnlDcmVlaykgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInJlZCIsIGhpZ2ggPSAiZ3JlZW4iKSsKICBnZ3RpdGxlKCJEcnkgQ3JlZWssIFROIikKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KZ2dzYXZlKCJEQ01hcEtERWltYWdlLmpwZyIsIHBsb3QgPSBLREVtYXAsIGRwaSA9IDMwMCwgbGltaXRzaXplID0gVFJVRSkKCmBgYAoKIVtDb21wbGVhdGVkIEtERSBIZWF0bWFwXSgvVXNlcnMvS2lyc3RlbmdsaXNoL0Rlc2t0b3AvRENNYXBLREVpbWFnZS5qcGcpCgoKCgoKQmlnIEhvbGxvdywgVE4KCmBgYHtyIEJpZyBIb2xsb3d9CkJpZ19Ib2xsb3cgPC0gcmVhZF9jc3YoIi9Vc2Vycy9LaXJzdGVuZ2xpc2gvRGVza3RvcC9CaWcgSG9sbG93LmNzdiIpClZpZXcoQmlnX0hvbGxvdykKYGBgCgoKCkp1c3QgbGlrZSBiZWZvcmUsIHJldHJpZXZlIGEgYmFzZW1hcCBvZiB5b3VyIHNpdGUgbG9jYXRpb24uIApgYGB7ciBHZXQgQkggYmFzZW1hcH0KYmFzZW1hcDwtIGdldF9tYXAobG9jYXRpb24gPSAiUmlkZ2V0b3AgVHJhaWwKQXNobGFuZCBDaXR5LCBUTiAzNzAxNSIsIHpvb209MTUpCmdnbWFwKGJhc2VtYXApCgpgYGAKCjxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4gUHV0IG91ciBMT1dBIGVuY291bnRlciBjb29yaWRpbmF0ZXMgb24gdGhlIGJhc2VtYXAgd2UganVzdCBjcmVhdGVkLjwvc3Bhbj4KCmBgYHtyIEJIIEJhc2UgbWFwIHdpdGggTE9XQWxvY2F0aW9uIHBvaW50c30KI3RpbWUgdG8gcHV0IHBvaW50cyBvbiB0aGUgbWFwCkJIbWFwPC1nZ21hcChiYXNlbWFwKSsgZ2VvbV9wb2ludChkYXRhPUJpZ19Ib2xsb3csIGFlcyhMb25naXR1ZGUsIExhdGl0dWRlKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IEJpZ19Ib2xsb3csIGFlcyhMb25naXR1ZGUsIExhdGl0dWRlKSwgc2l6ZSA9IDAuMSkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZT0iYm9sZCIpKSArIGxhYnMoeD0iTG9uZ2l0dWRlIiwgeT0iTGF0aXR1ZGUiKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemU9MSkpICsgdGhlbWVfYncoKQpwbG90KEJIbWFwKQpgYGAKCgo8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+IFB1dCBkYXRhIGludG8gc3BhdGlhbCBwb2ludHMgbGlrZSBiZWZvcmUgdG8gYmUgYWJsZSB0byBjcmVhdGUgdGhlIEtERSBhbmQgTUNQLgp3ZSB3aWxsIGJlIHVzaW5nIENSUyAgKGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbSkgd2hpY2ggaXMgdGhlIGZvcm1hdCBmb3IgbW9zdCBHUFMgZGF0YS4gIDwvc3Bhbj4KCmBgYHtyIFNQIGZvcm1hdHRpbmcsIGluY2x1ZGU9RkFMU0V9CkJIcHRzIDwtIHNwOjpTcGF0aWFsUG9pbnRzKGNvb3JkcyA9IGNiaW5kKEJpZ19Ib2xsb3ckTG9uZ2l0dWRlLCBCaWdfSG9sbG93JExhdGl0dWRlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmcgPSBzcDo6Q1JTKCIraW5pdD1lcHNnOjQzMjYiKSkKaGVhZChCSHB0cykKCiMjIHB1dCBhbGwgdGhlIGRhdGEgaW50byBhIHNpbmdsZSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lIChzcGRmKQpCSF9zcGRmIDwtIHNwOjpTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKEJIcHRzLCBCaWdfSG9sbG93KQpoZWFkKEJIX3NwZGYpCgojYWx0aG91Z2ggd2UgZG9uJ3QgaGF2ZSBzZXBlcmF0ZSBzcGVjaWVzIHdlIG5lZWQgdG8gbGV0IFIga25vdyB0aGF0IGFsbCBwb2ludHMgYXJlIGZvciBvbmUgc3RyZWFtIHN5c3RlbSBhbmQgdGhpcyB3aWxsIGNyZWF0ZSBhIGxpc3Qgb2Ygb25lCkJIX3NlcCA8LSBzcGxpdCh4ID0gQkhfc3BkZiwgZiA9IEJIX3NwZGYkTmFtZSwgZHJvcCA9IEZBTFNFKQpgYGAKCjxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4gVGltZSB0byBtYWtlIHRoZSBNQ1AuPC9zcGFuPgoKYGBge3IgQkggTUNQfQpCSG1jcCA8LSBsYXBwbHkoQkhfc2VwLCBGVU4gPSBmdW5jdGlvbih4KXtyZ2Vvczo6Z0NvbnZleEh1bGwoeCl9KQoKIyN0aGlzIG1ha2VzIHBvbHlnb24gZnJvbSB0aGUgbGlzdCBvZiBvbmUgd2hlbiB3ZSB0b2xkIFIgdGhhdCBhbGwgdmFyaWFibGVzIGFyZSBmcm9tIG9uZSBzdHJlYW0gc3lzdGVtCkJIbWNwIDwtIG1hcHBseShCSG1jcCwgbmFtZXMoQkhtY3ApLCAKICAgICAgICAgICAgICAgIFNJTVBMSUZZID0gRkFMU0UsCiAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4LHkpe3hAcG9seWdvbnNbWzFdXUBJRCA8LSB5CiAgICAgICAgICAgICAgICByZXR1cm4oeCl9KQoKQkhtY3AgPC0gZG8uY2FsbChyYmluZCxCSG1jcCkKQkhtY3AgPC0gU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lKFNyID0gQkhtY3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YS5mcmFtZShCaXJkID0gbmFtZXMoQkhtY3ApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoLklEID0gRkFMU0UpCnBsb3QoQkhtY3ApCmBgYAoKPHNwYW4gc3R5bGU9ImNvbG9yOnBpbmsiPk5vdyB0aGF0IHdlIGhhdmUgYW4gTUNQIGZvciB0aGUgc3RyZWFtLCBvbnRvIEtERS48L3NwYW4+CgpgYGB7ciBCSCBLREUsIHdhcm5pbmc9RkFMU0V9CmJ3IDwtIGxhcHBseShCSF9zZXAsIEZVTiA9IGZ1bmN0aW9uKHgpe2tzOjpIbHNjdih4QGNvb3Jkcyl9KQojIyBTdGVwIHR3bzogZ2VuZXJhdGUga2RlCgpCSF9rZGUgPC1tYXBwbHkoQkhfc2VwLGJ3LAogICAgICAgICAgICAgICAgU0lNUExJRlkgPSBGQUxTRSwKICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgseSl7CiAgICAgICAgICAgICAgICAgIHJhc3RlcihrZGUoeEBjb29yZHMsaD15KSl9KQoKIyBUaGlzIGNvZGUgbWFrZXMgYSBjdXN0b20gZnVuY3Rpb24gY2FsbGVkIGdldENvbnRvdXIuIAojIElucHV0czoKIyAgICBrZGUgPSBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZQojICAgIHByb2IgPSBwcm9iYWJpbHkgLSBkZWZhdWx0IGlzIDAuOTUKCmdldENvbnRvdXIgPC0gZnVuY3Rpb24oa2RlLCBwcm9iID0gMC45NSl7CiAgIyBzZXQgYWxsIHZhbHVlcyAwIHRvIE5BCiAga2RlW2tkZSA9PSAwXTwtTkEKICAjIGNyZWF0ZSBhIHZlY3RvciBvZiByYXN0ZXIgdmFsdWVzCiAga2RlX3ZhbHVlcyA8LSByYXN0ZXI6OmdldFZhbHVlcyhrZGUpCiAgIyBzb3J0IHZhbHVlcyAKICBzb3J0ZWRWYWx1ZXMgPC0gc29ydChrZGVfdmFsdWVzWyFpcy5uYShrZGVfdmFsdWVzKV0sZGVjcmVhc2luZyA9IFRSVUUpCiAgIyBmaW5kIGN1bXVsYXRpdmUgc3VtIHVwIHRvIGl0aCBsb2NhdGlvbgogIHN1bXMgPC0gY3Vtc3VtKGFzLm51bWVyaWMoc29ydGVkVmFsdWVzKSkKICAjIGJpbmFyeSByZXNwb25zZSBpcyB2YWx1ZSBpbiB0aGUgcHJvYmFiaWx5IHpvbmUgb3Igbm90CiAgcCA8LSBzdW0oc3VtcyA8PSBwcm9iICogc3Vtc1tsZW5ndGgoc3VtcyldKQogICMgU2V0IHZhbHVlcyBpbiByYXN0ZXIgdG8gMSBvciAwCiAga2RlcHJvYiA8LSByYXN0ZXI6OnNldFZhbHVlcyhrZGUsIGtkZV92YWx1ZXMgPj0gc29ydGVkVmFsdWVzW3BdKQogICMgcmV0dXJuIG5ldyBrZGUKICByZXR1cm4oa2RlcHJvYil9CgpCSF85NWtkZSA8LSBsYXBwbHkoQkhfa2RlLAogICAgICAgICAgICAgICAgICAgRlVOID0gZ2V0Q29udG91cixwcm9iID0gMC45NSkKCmBgYAoKPHNwYW4gc3R5bGU9ImNvbG9yOnBpbmsiPlRoZXNlIG5leHQgcGxvdHMgcHV0IE1DUCBvbiB0b3Agb2YgS0RFCjwvc3Bhbj4KYGBge3IgQmlnIEhvbGxvdyBNYXB9CgpwbG90KEJIX2tkZVtbMV1dKSsKICBwbG90KEJIbWNwWzEsXSxhZGQgPSBUUlVFKQoKCmBgYAo8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+IFRpbWUgdG8gb3ZlcmxhcCBLREUgb250byB0aGUgYmFzZSBtYXAuIENvZGUgZm91bmQgZnJvbSBodHRwOi8vZGF0YS1hbmFseXRpY3MubmV0L2NlcC9TY2hlZHVsZV9maWxlcy9nZW9zcGF0aWFsLmh0bWwgPC9zcGFuPgoKYGBge3IgS0RFIHdpdGggQkggbWFwLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpCSGtkZTwtIEJIbWFwICsKICBzdGF0X2RlbnNpdHkyZChhZXMoeCA9IExvbmdpdHVkZSwgeSA9IExhdGl0dWRlLCBmaWxsID0gLi5sZXZlbC4uLGFscGhhPS4ubGV2ZWwuLiksIGJpbnMgPSAxMCwgZ2VvbSA9ICJwb2x5Z29uIiwgZGF0YSA9QmlnX0hvbGxvdykgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInJlZCIsIGhpZ2ggPSAiZ3JlZW4iKSsKICBnZ3RpdGxlKCJCaWcgSG9sbG93LCBUTiIpCmBgYAoKYGBge0JIbWFwa2RlIGluY2x1ZGU9RkFMU0V9Cmdnc2F2ZSgiQkhNYXBLREVpbWFnZS5qcGciLCBwbG90ID0gQkhrZGUsIGRwaSA9IDMwMCwgbGltaXRzaXplID0gVFJVRSkKYGBgCgohW0NvbXBsZWF0ZWQgS0RFIEhlYXRtYXBdKC9Vc2Vycy9LaXJzdGVuZ2xpc2gvRGVza3RvcC9CSE1hcEtERWltYWdlLmpwZykKCgoKCgpXYWxsIEJyYW5jaCwgVE4KCmBgYHtyIFdhbGwgQnJhbmNoIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpXYWxsX0JyYW5jaCA8LSByZWFkX2NzdigiL1VzZXJzL0tpcnN0ZW5nbGlzaC9EZXNrdG9wL1dhbGwgQnJhbmNoLmNzdiIpClZpZXcoV2FsbF9CcmFuY2gpCmBgYAoKCkNyZWF0ZSB0aGUgYmFzZW1hcCBmb3IgdGhlIHNpdGUgbG9jYXRpb24uIFRoaXMgc2l0ZSBpcyBXYWxsIEJyYW5jaCBzdHJlYW0gaW4gVGVubmVzc2UKCmBgYHtyIEdldCBXQiBiYXNlbWFwfQpiYXNlbWFwPC0gZ2V0X21hcCgobG9jYXRpb24gPSAiIDI1NjEgQWxleCBPdmVybG9vayBXYXkKQ2xhcmtzdmlsbGUsIFROIDM3MDQzLCAzNi40OTc4MTgsIC04Ny4yNjc0MjgiKSwgem9vbT0xNSwgbWFwdHlwZSA9ICJzYXRlbGxpdGUiKQpnZ21hcChiYXNlbWFwKQoKYGBgCgo8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+IExPV0EgZW5jb3VudGVyIGNvb3JpZGluYXRlcyBvbiB0aGUgYmFzZW1hcCB3ZSBqdXN0IGNyZWF0ZWQuPC9zcGFuPgoKYGBge3IgV0IgYmFzZSBtYXAgd2l0aCBMT1dBbG9jYXRpb24gcG9pbnRzfQojdGltZSB0byBwdXQgcG9pbnRzIG9uIHRoZSBtYXAKV0JtYXA8LWdnbWFwKGJhc2VtYXApKyBnZW9tX3BvaW50KGRhdGE9V2FsbF9CcmFuY2gsIGFlcyhMb25naXR1ZGUsIExhdGl0dWRlKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IFdhbGxfQnJhbmNoLCBhZXMoTG9uZ2l0dWRlLCBMYXRpdHVkZSksIGNvbG9yPSAiYmxhY2siLCBzaXplID0gMC4xKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpICsgbGFicyh4PSJMb25naXR1ZGUiLCB5PSJMYXRpdHVkZSIpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0xKSkgKyB0aGVtZV9jbGFzc2ljKCkKcGxvdChXQm1hcCkKYGBgCgogCmBgYHtyIFdCc3B9CldCcHRzIDwtIHNwOjpTcGF0aWFsUG9pbnRzKGNvb3JkcyA9IGNiaW5kKFdhbGxfQnJhbmNoJExvbmdpdHVkZSwgV2FsbF9CcmFuY2gkTGF0aXR1ZGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qNHN0cmluZyA9IHNwOjpDUlMoIitpbml0PWVwc2c6NDMyNiIpKQpoZWFkKFdCcHRzKQoKV0Jfc3BkZiA8LSBzcDo6U3BhdGlhbFBvaW50c0RhdGFGcmFtZShXQnB0cywgV2FsbF9CcmFuY2gpCmhlYWQoV0Jfc3BkZikKCldCX3NlcCA8LSBzcGxpdCh4ID0gV0Jfc3BkZiwgZiA9IFdCX3NwZGYkTmFtZSwgZHJvcCA9IEZBTFNFKQpgYGAKCjxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4gVGltZSB0byBtYWtlIHRoZSBNQ1AuPC9zcGFuPgoKYGBge3IgV0IgTUNQfQpXQm1jcCA8LSBsYXBwbHkoV0Jfc2VwLCBGVU4gPSBmdW5jdGlvbih4KXtyZ2Vvczo6Z0NvbnZleEh1bGwoeCl9KQoKIyN0aGlzIG1ha2VzIHBvbHlnb24gZnJvbSB0aGUgbGlzdCBvZiBvbmUgd2hlbiB3ZSB0b2xkIFIgdGhhdCBhbGwgdmFyaWFibGVzIGFyZSBmcm9tIG9uZSBzdHJlYW0gc3lzdGVtCldCbWNwIDwtIG1hcHBseShXQm1jcCwgbmFtZXMoV0JtY3ApLCAKICAgICAgICAgICAgICAgIFNJTVBMSUZZID0gRkFMU0UsCiAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4LHkpe3hAcG9seWdvbnNbWzFdXUBJRCA8LSB5CiAgICAgICAgICAgICAgICByZXR1cm4oeCl9KQpXQm1jcCA8LSBkby5jYWxsKHJiaW5kLFdCbWNwKQpXQm1jcCA8LSBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUoU3IgPSBXQm1jcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhLmZyYW1lKEJpcmQgPSBuYW1lcyhXQm1jcCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF0Y2guSUQgPSBGQUxTRSkKcGxvdChXQm1jcCkKCmBgYAoKPHNwYW4gc3R5bGU9ImNvbG9yOnBpbmsiPk5vdyB0aGF0IHdlIGhhdmUgYW4gTUNQIGZvciB0aGUgc3RyZWFtLCBvbnRvIEtERS48L3NwYW4+CgpgYGB7ciBXQiBLREUsIHdhcm5pbmc9RkFMU0V9CmJ3IDwtIGxhcHBseShXQl9zZXAsIEZVTiA9IGZ1bmN0aW9uKHgpe2tzOjpIbHNjdih4QGNvb3Jkcyl9KQoKIyMgU3RlcCB0d286IGdlbmVyYXRlIGtkZQpXQl9rZGUgPC1tYXBwbHkoV0Jfc2VwLGJ3LAogICAgICAgICAgICAgICAgU0lNUExJRlkgPSBGQUxTRSwKICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgseSl7CiAgICAgICAgICAgICAgICAgIHJhc3RlcihrZGUoeEBjb29yZHMsaD15KSl9KQojIFRoaXMgY29kZSBtYWtlcyBhIGN1c3RvbSBmdW5jdGlvbiBjYWxsZWQgZ2V0Q29udG91ci4gCiMgSW5wdXRzOgojICAgIGtkZSA9IGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlCiMgICAgcHJvYiA9IHByb2JhYmlseSAtIGRlZmF1bHQgaXMgMC45NQoKZ2V0Q29udG91ciA8LSBmdW5jdGlvbihrZGUsIHByb2IgPSAwLjk1KXsKICAjIHNldCBhbGwgdmFsdWVzIDAgdG8gTkEKICBrZGVba2RlID09IDBdPC1OQQogICMgY3JlYXRlIGEgdmVjdG9yIG9mIHJhc3RlciB2YWx1ZXMKICBrZGVfdmFsdWVzIDwtIHJhc3Rlcjo6Z2V0VmFsdWVzKGtkZSkKICAjIHNvcnQgdmFsdWVzIAogIHNvcnRlZFZhbHVlcyA8LSBzb3J0KGtkZV92YWx1ZXNbIWlzLm5hKGtkZV92YWx1ZXMpXSxkZWNyZWFzaW5nID0gVFJVRSkKICAjIGZpbmQgY3VtdWxhdGl2ZSBzdW0gdXAgdG8gaXRoIGxvY2F0aW9uCiAgc3VtcyA8LSBjdW1zdW0oYXMubnVtZXJpYyhzb3J0ZWRWYWx1ZXMpKQogICMgYmluYXJ5IHJlc3BvbnNlIGlzIHZhbHVlIGluIHRoZSBwcm9iYWJpbHkgem9uZSBvciBub3QKICBwIDwtIHN1bShzdW1zIDw9IHByb2IgKiBzdW1zW2xlbmd0aChzdW1zKV0pCiAgIyBTZXQgdmFsdWVzIGluIHJhc3RlciB0byAxIG9yIDAKICBrZGVwcm9iIDwtIHJhc3Rlcjo6c2V0VmFsdWVzKGtkZSwga2RlX3ZhbHVlcyA+PSBzb3J0ZWRWYWx1ZXNbcF0pCiAgIyByZXR1cm4gbmV3IGtkZQogIHJldHVybihrZGVwcm9iKX0KCldCXzk1a2RlIDwtIGxhcHBseShXQl9rZGUsCiAgICAgICAgICAgICAgICAgICBGVU4gPSBnZXRDb250b3VyLHByb2IgPSAwLjk1KQoKYGBgCgo8c3BhbiBzdHlsZT0iY29sb3I6cGluayI+VGhlc2UgbmV4dCBwbG90cyBwdXQgTUNQIG9uIHRvcCBvZiBLREUKPC9zcGFuPgpgYGB7ciBXQiBNYXB9CiMjIFRoZXNlIG5leHQgcGxvdHMgcHV0IE1DUCBvbiB0b3Agb2YgS0RFLCAodGVycml0b3J5IGZvciB0aGlzIG1hcCBpcyB2ZXJ5IGxpbmVhcikKcGxvdChXQl9rZGVbWzFdXSkrCiAgcGxvdChXQm1jcFsxLF0sYWRkID0gVFJVRSkKYGBgCjxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4gVGltZSB0byBvdmVybGFwIEtERSBvbnRvIHRoZSBiYXNlIG1hcC4gQ29kZSBmb3VuZCBmcm9tIGh0dHA6Ly9kYXRhLWFuYWx5dGljcy5uZXQvY2VwL1NjaGVkdWxlX2ZpbGVzL2dlb3NwYXRpYWwuaHRtbCA8L3NwYW4+CgpgYGB7ciBXQiBLREUgd2l0aCBtYXB9CldCa2RlPC1XQm1hcCArCiAgc3RhdF9kZW5zaXR5MmQoYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSwgZmlsbCA9IC4ubGV2ZWwuLixhbHBoYT0uLmxldmVsLi4pLCBiaW5zID0gMTAsIGdlb20gPSAicG9seWdvbiIsIGRhdGEgPSBXYWxsX0JyYW5jaCkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInJlZCIsIGhpZ2ggPSAiZ3JlZW4iKSsKICBnZ3RpdGxlKCJXYWxsIEJyYW5jaCwgVE4iKQpgYGAKCmBgYHtyIGtkZW1hcCBpbWFnZSwgaW5jbHVkZT1GQUxTRX0KZ2dzYXZlKCJXQk1hcEtERWltYWdlLmpwZyIsIHBsb3QgPSBXQmtkZSwgZHBpID0gMzAwLCBsaW1pdHNpemUgPSBUUlVFKQpgYGAKCiFbQ29tcGxlYXRlZCBLREUgSGVhdG1hcF0oL1VzZXJzL0tpcnN0ZW5nbGlzaC9EZXNrdG9wL1dCTWFwS0RFaW1hZ2UuanBnKQoKCgoKCgo=